import axios, {AxiosRequestConfig, AxiosResponse, Method} from 'axios'
import router from '@/router'
import store from '@/store'
import {clearStorage} from './localstoreage'
import {logout} from './toolkit'
import {baseUrl} from './baseUrl'
import {message} from '@/libs/global/antDesignVue'

// const whiteApiList = ['/user/webLogin', '/user/webWxScanLogin', '/user/webRegisterAndLogin']

/**
 * 跳转登录页
 * 携带当前页面路由，以期在登录页面完成登录后返回当前页面
 */
const toLogin = () => {
  router.replace({
    name: 'login'
  })
}

/**
 * 请求失败后的错误统一处理
 * @param {Number} status 请求失败的状态码
 */
const errorHandle = (status: number, other: string) => {
  // , other: string
  // 状态码判断
  switch (status) {
    case 302:
      message.error('接口重定向了！')
      break
    case 400:
      message.error('发出的请求有错误，服务器没有进行新建或修改数据的操作==>' + status)
      break
    // 401: 未登录
    // 未登录则跳转登录页面，并携带当前页面的路径
    // 在登录成功后返回当前页面，这一步需要在登录页操作。
    case 401: //重定向
      message.error('token:登录失效==>' + status + ':' + store.getters.GETROLE)
      // storage.remove(store.state.Roles)
      clearStorage()
      // storage.get(store.state.Roles) //意义不明
      // toLogin()
      break
    // 403 token过期
    // 清除token并跳转登录页
    case 403:
      message.error('登录过期,用户得到授权，但是访问是被禁止的==>' + status)
      // store.commit('token', null);
      setTimeout(() => {
        toLogin()
      }, 1000)
      break
    case 404:
      message.error('网络请求不存在==>' + status)
      break
    case 406:
      message.error('请求的格式不可得==>' + status)
      break
    case 408:
      message.error(' 请求超时！')
      break
    case 410:
      message.error('请求的资源被永久删除，且不会再得到的==>' + status)
      break
    case 422:
      message.error('当创建一个对象时，发生一个验证错误==>' + status)
      break
    case 500:
      message.error('服务器发生错误，请检查服务器==>' + status)
      break
    case 502:
      message.error('网关错误==>' + status)
      break
    case 503:
      message.error('服务不可用，服务器暂时过载或维护==>' + status)
      break
    case 504:
      message.error('网关超时==>' + status)
      break
    default:
      message.error('其他错误错误==>' + status)
  }
}

// 定义接口
interface PendingType {
  url?: string
  method?: Method | string
  params: any
  data: any
  cancel: any
}
// 取消重复请求
const pending: Array<PendingType> = []
const CancelToken = axios.CancelToken

// 移除重复请求
const removePending = (config: AxiosRequestConfig) => {
  for (const key in pending) {
    const item: number = +key
    const list: PendingType = pending[key]

    // 当前请求在数组中存在时执行函数体
    if (
      list.url === config.url &&
      list.method === config.method &&
      JSON.stringify(list.params) === JSON.stringify(config.params) &&
      JSON.stringify(list.data) === JSON.stringify(config.data)
    ) {
      // 执行取消操作
      list.cancel('操作太频繁，请稍后再试')
      // 从数组中移除记录
      pending.splice(item, 1)
    }
  }
}

/* 实例化请求配置 */
const instance = axios.create({
  // 请求时长
  timeout: 1000 * 60 * 10,
  // 请求的base地址 TODO:这块以后根据不同的模块调不同的api
  baseURL: baseUrl,
  // 表示跨域请求时是否需要使用凭证
  withCredentials: false
})

/**
 * 请求拦截器
 * 每次请求前，如果存在token则在请求头中携带token
 */
instance.interceptors.request.use(
  config => {
    try {
      // @ts-ignore
      if (!config.noPadding) {
        removePending(config)
        config.cancelToken = new CancelToken((c: any) => {
          pending.push({
            url: config.url,
            method: config.method,
            params: config.params,
            data: config.data,
            cancel: c
          })
        })
      }

      // 登录流程控制中，根据本地是否存在token判断用户的登录情况
      // 但是即使token存在，也有可能token是过期的，所以在每次的请求头中携带token
      // 后台根据携带的token判断用户的登录情况，并返回给我们对应的状态码
      // 而后我们可以在响应拦截器中，根据状态码进行一些统一的操作。

      // if (!whiteApiList.includes(config.url as string)) {
      //   const token = getStorage('token')

      //   if (!token) {
      //     console.log('没有token,清空状态,返回登录页')
      //     logout()
      //     return config
      //   }

      //   const userId = getUserId()
      //   if (!userId) {
      //     console.log('没有userId,清空状态,返回登录页')
      //     logout()
      //     return config
      //   }

      //   if (config.headers) {
      //     config.headers.token = token
      //     config.headers.userId = userId
      //   } else {
      //     config.headers = {
      //       token,
      //       userId
      //     }
      //   }
      // }

      return config
    } catch (err) {
      console.log(err)
      return Promise.reject(false)
    }
  },
  (error: any) => {
    console.log(error)

    message.error(error.data.error.message)
    return Promise.reject(error.data.error.message)
  }
)

/**
 * 响应拦截器
 * 接口返回数据之前调用
 */
instance.interceptors.response.use(
  config => {
    removePending(config.config)
    // 请求成功
    if (config.status === 200 || config.status === 204) {
      const result = config.data.result
      const data = config.data.data
      const msg = config.data.msg

      /**身份过期，重新登录 */
      if (config.data == 50000) {
        logout()
        return Promise.reject(false)
      }

      /**权限错误 */
      if (config.data == 40000) {
        message.warning(msg)
        return Promise.reject(false)
      }

      /**调用成功，返回成功状态时 */
      if (result === 'success' && !data && msg) {
        message.success(msg)
      }

      /**调用失败 */
      if (result === 'error') {
        if (!data && msg) {
          message.error(msg)
        }
        return Promise.reject(false)
      }

      return Promise.resolve(config)
    }

    return Promise.reject(false)
  },
  // 请求失败
  error => {
    console.log(error)

    const {response} = error

    if (axios.isCancel(error)) {
      // 取消请求的错误
      console.log(error.message) // 做出相应的处理

      // 中断 promise 链接
      return Promise.reject(false)
    } else if (response) {
      // 请求出错了
      errorHandle(response.status, response.data.message)
      //? 响应报错重新请求 -----------------------------------
      // // 超时重新请求
      // const config = error.config;
      // // 全局的请求次数,请求的间隙
      // const [RETRY_COUNT, RETRY_DELAY] = [3, 1000];

      // if (config && RETRY_COUNT) {
      //   // 设置用于跟踪重试计数的变量
      //   config.__retryCount = config.__retryCount || 0;
      //   // 检查是否已经把重试的总数用完
      //   if (config.__retryCount >= RETRY_COUNT) {
      //     return Promise.reject(response || { message: error.message });
      //   }
      //   // 增加重试计数
      //   config.__retryCount++;
      //   // 创造新的Promise来处理指数后退
      //   const backoff = new Promise<void>((resolve) => {
      //     setTimeout(() => {
      //       resolve();
      //     }, RETRY_DELAY || 1);
      //   });
      //   // instance重试请求的Promise
      //   return backoff.then(() => {
      //     return instance(config);
      //   });
      // }
      //? 响应报错重新请求 -----------------------------------

      return Promise.reject(response)
    }
    // 处理断网的情况
    // eg:请求超时或断网时，更新state的network状态
    // network状态在app.vue中控制着一个全局的断网提示组件的显示隐藏
    // 后续增加断网情况下做的一些操作
    // store.commit('networkState', false);
    return Promise.reject(false)
  }
)

// 只需要考虑单一职责，这块只封装axios
// export default instance
export default class Request {
  /**
   * get方法
   * @param {string} url 路径
   * @param {any} params 参数
   * @param {any} config 其他配置
   */
  static get<T>(url: string, params?: any, config?: any) {
    return new Promise<CustomRequest<T>>((resolve, reject) => {
      const defaultConfig = {}
      if (config) {
        Object.assign(defaultConfig, config)
      }

      instance({
        url,
        method: 'get',
        params,
        ...defaultConfig
      })
        .then((res: any) => {
          console.log('get 请求成功', url, res)
          resolve(res.data)
        })
        .catch((err: any) => {
          console.error('get 请求失败', url, err)
          reject(err)
        })
    })
  }
  /**
   * post方法
   * @param {string} url 路径
   * @param {any} params 参数
   * @param {any} config 其他配置
   */
  static post<T>(url: string, params?: any, config?: any) {
    return new Promise<CustomRequest<T>>((resolve, reject) => {
      const defaultConfig = {}
      if (config) {
        Object.assign(defaultConfig, config)
      }
      instance({
        url,
        method: 'post',
        data: params,
        ...defaultConfig
      })
        .then((res: AxiosResponse) => {
          console.log('post 请求成功', url, res)
          resolve(res.data)
        })
        .catch((err: any) => {
          console.error('post 请求失败', url, err)
          reject(false)
        })
    })
  }
  /**
   * 获取文件 post方法
   * @param {string} url 路径
   * @param {any} params 参数
   * @param {any} config 其他配置
   */
  static filePost(url: string, params?: any, config?: any) {
    return new Promise<AxiosResponse<Blob>>((resolve, reject) => {
      const defaultConfig = {}

      if (config) {
        Object.assign(defaultConfig, config)
      }

      instance({
        url,
        method: 'post',
        data: params,
        responseType: 'blob',

        ...defaultConfig
      })
        .then((res: AxiosResponse) => {
          console.log('filePost 请求成功', url, res)
          resolve(res)
        })
        .catch((err: any) => {
          console.error('filePost 请求失败', url, err)
          reject(false)
        })
    })
  }
}
